// Bristleback plugin - Copyright (c) 2010 bristleback.googlecode.com
// ---------------------------------------------------------------------------
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
// This library is distributed in the hope that it will be useful,
// but without any warranty; without even the implied warranty of merchantability
// or fitness for a particular purpose.
// You should have received a copy of the GNU Lesser General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
// ---------------------------------------------------------------------------
package pl.bristleback.server.bristle;

import org.apache.log4j.Logger;
import org.jwebsocket.api.PluginConfiguration;
import org.jwebsocket.api.WebSocketConnector;
import org.jwebsocket.api.WebSocketEngine;
import org.jwebsocket.api.WebSocketServer;
import org.jwebsocket.kit.CloseReason;
import org.jwebsocket.kit.PlugInResponse;
import org.jwebsocket.plugins.TokenPlugIn;
import org.jwebsocket.token.Token;
import org.jwebsocket.token.TokenFactory;
import pl.bristleback.server.bristle.actions.ActionsDispatcher;
import pl.bristleback.server.bristle.actions.ActionsInitializer;
import pl.bristleback.server.bristle.actions.ReservedActionName;
import pl.bristleback.server.bristle.config.BristleConfiguration;
import pl.bristleback.server.bristle.config.BristleConstants;
import pl.bristleback.server.bristle.config.ExceptionHandlersInitializer;
import pl.bristleback.server.bristle.messages.MessageController;
import pl.bristleback.server.bristle.rights.ConnectorRightsUtil;
import pl.bristleback.server.bristle.states.ServerStateInspector;

/**
 * Bristleback Server plugin (also called Bristle plugin) is a helpful framework overlay,
 * which simplifies process of creating applications using jWebsocket library.
 * Bristleback plugin consists of five main elements:
 * <ul>
 * <li>
 * Remote actions.<br/>
 * Remote actions are used to declare response for user actions. They consists of name, required user rights and action response.
 * Adding new action is very easy because of annotation usage.
 * Actions are closely integrated with connector rights, which are described below.
 * In addition, any exceptions while dispatching actions can be caught by implementing
 * {@link pl.bristleback.server.bristle.exceptions.handlers.ExceptionHandler} interface.
 * </li>
 * <li>
 * Message senders and message dispatcher<br/>
 * Senders and dispatchers provide way for sending messages to client in easy and thread safe way.
 * Senders allow avoiding low level mechanisms of jwebsocket.
 * </li>
 * <li>
 * Processing tokens to java objects and java objects to tokens<br/>
 * With annotations usage, you can easily bind incoming token into plain java objects, with additional validation.
 * Binding and tokenizing operations support nested properties and collections/maps/arrays.
 * </li>
 * <li>
 * Connector rights<br/>
 * After connector is connected, plugin creates empty rights set desired for that connector.
 * Connector rights are used to determine whether connector can execute action.
 * Rights usage can be extended, eg. in sending messages.
 * </li>
 * <li>
 * Client side of Bristleback plugin<br/>
 * Client side plugin provides the same action dispatching mechanism as on server side.
 * </li>
 * <li>
 * Spring Framework integration<br/>
 * Spring Framework is used in thousands of projects. Bristleback applications are able to be fully Spring integrated.
 * Action classes and every class loaded via configuration file, like message senders or server state listeners, may be Spring beans.
 * Check the documentation of Bristleback Plugin elements to see how can you make use of Spring beans.
 * </li>
 * </ul>
 * <p/>
 * If you need to perform some operations before server is up or after server is down,
 * you might be interested in using server state listeners chain. The only thing to do is to add your own
 * {@link pl.bristleback.server.bristle.states.ServerStateListener} implementation.
 * You can add as many {@link pl.bristleback.server.bristle.states.ServerStateListener} as you want.
 * <p/>
 * Almost every element of plugin is configurable, user can specify how actions or message will be looked.
 * Yet, default implementation of those elements allow user to create "Hello world" application in just five minutes.
 * Plugin settings in jwebsocket.xml file are used to configure Bristle plugin.
 * Please note that this is early version of plugin, so nothing is tested as good as we would like it to be.
 * <p/>
 * {@link pl.bristleback.server.bristle.config.BristleConstants} class and {@link pl.bristleback.server.bristle.config.BristleConfiguration}
 * have fields which can be helpful in creating your first application.
 * <p/>
 * Current version of Bristle plugin contains two default message senders and one message dispatcher.
 * To make use of message senders, users must add them into their jwebsocket configuration file.
 * Example configuration is shown in this project, in  "xml/jwebsocket.xml" file.
 * <p/>
 * Created on: 2010-09-03 16:01:30 <br/>
 *
 * @author Wojciech Niemiec
 * @version 0.1.0
 * @see <a href="http://bristleback.googlecode.com">Bristleback plugin website</a>
 * @see pl.bristleback.server.bristle.config.BristleConfiguration Bristleback configuration
 * @see pl.bristleback.server.bristle.config.BristleConfigurationElement Bristleback configuration elements
 */
public class BristlebackServerPlugin extends TokenPlugIn {
  private static Logger log = Logger.getLogger(BristlebackServerPlugin.class.getName());

  private BristleConfiguration bristleConfiguration;
  private ActionsDispatcher actionsDispatcher;
  private MessageController messageController;
  private ServerStateInspector serverStateInspector;


  /**
   * Constructor required by TokenPlugIn class.
   *
   * @param configuration plugin configuration.
   */
  public BristlebackServerPlugin(PluginConfiguration configuration) {
    super(configuration);
    this.setNamespace(BristleConstants.BRISTLE_PLUGIN_NAMESPACE);
    setCoreElements();
  }

  private void setCoreElements() {
    bristleConfiguration = new BristleConfiguration();
    actionsDispatcher = new ActionsDispatcher();
    messageController = new MessageController();
    serverStateInspector = new ServerStateInspector();
  }

  /**
   * Loading of all core elements from configuration and annotations.
   * Firstly, plugin loads actions and exception handlers.
   * Next step is to load message controller (dispatcher and senders) and to export senders.
   * After that, server state inspector is loaded and invokes server state listeners chain.
   * As soon as every element is ready, message controller starts dispatching messages to connectors.
   *
   * @param engine websocket engine.
   */
  public void engineStarted(WebSocketEngine engine) {
    WebSocketServer server = getServer();
    loadConfiguration();
    loadActions();
    loadExceptionsHandlers();
    loadMessageHandler(server);
    notifyServerStateInspector();
    messageController.startDispatcher();
  }

  private void loadConfiguration() {
    bristleConfiguration.load(getSettings());
  }

  private void loadActions() {
    ActionsInitializer actionsInitializer = new ActionsInitializer();
    actionsInitializer.loadActionsContainer(bristleConfiguration);
    actionsDispatcher.setContainer(actionsInitializer.getContainer());
  }

  private void loadExceptionsHandlers() {
    ExceptionHandlersInitializer exceptionHandlersInitializer = new ExceptionHandlersInitializer();
    exceptionHandlersInitializer.loadExceptionsHandlers(getSettings());
    actionsDispatcher.setExceptionHandlersContainer(exceptionHandlersInitializer.getExceptionHandlersContainer());
  }

  private void loadMessageHandler(WebSocketServer server) {
    messageController.loadMessageContainer(bristleConfiguration);
    messageController.assignServer(server);
    messageController.exportSenders();
  }

  private void notifyServerStateInspector() {
    serverStateInspector.loadServerStateListeners(getSettings());
    serverStateInspector.serverStart();
  }

  /**
   * In this method closing operations are performed. Dispatcher is stopped,
   * server state inspector is notified about server shutdown.
   *
   * @param engine websocket engine.
   */
  public void engineStopped(WebSocketEngine engine) {
    messageController.stopDispatcher();
    serverStateInspector.serverShutdown();
    //todo-wojtek - free resources [unbind actions, message senders, etc]
  }

  /**
   * When connector is started, new empty rights set is created and assigned to that connector.
   * Moreover, it's possible to have a special action class with reserved name
   * {@link pl.bristleback.server.bristle.actions.ReservedActionName#CONNECTION_STARTED_ACTION_NAME}.
   *
   * @param connector websocket connector.
   */
  public void connectorStarted(WebSocketConnector connector) {
    ConnectorRightsUtil.createRightsSetForConnector(connector);
    Token token = TokenFactory.createToken();
    actionsDispatcher.performReservedAction(connector, ReservedActionName.CONNECTION_STARTED_ACTION_NAME, token);
  }

  /**
   * Additional operations performed when connector is stopped.
   * It's possible to have a special action class with reserved name
   * {@link pl.bristleback.server.bristle.actions.ReservedActionName#CONNECTION_STOPPED_ACTION_NAME}.
   * Reason of closed connection is given in variable named {@code closeReason}.
   *
   * @param connector   websocket connector.
   * @param closeReason connection close reason.
   */
  public void connectorStopped(WebSocketConnector connector, CloseReason closeReason) {
    Token token = TokenFactory.createToken();
    token.setString("closeReason", closeReason.toString());
    actionsDispatcher.performReservedAction(connector, ReservedActionName.CONNECTION_STOPPED_ACTION_NAME, token);
  }

  /**
   * Process token sent by user. If token namespace is equals to
   * {@link pl.bristleback.server.bristle.config.BristleConstants#BRISTLE_PLUGIN_NAMESPACE}, then plugin chain is aborted
   * and dedicated dispatcher performs action. This is one of the core elements in Bristle plugin.
   * More about dispatching actions can be found in {@link pl.bristleback.server.bristle.actions.ActionsDispatcher} documentation.
   *
   * @param response  wrapper object containing information about plugin chain state.
   * @param connector websocket connector.
   * @param token     content of the message.
   */
  public void processToken(PlugInResponse response, WebSocketConnector connector, Token token) {
    String namespace = token.getNS();
    if (BristleConstants.BRISTLE_PLUGIN_NAMESPACE.equals(namespace)) {
      response.abortChain();
      performAction(connector, token);
    }
  }

  private void performAction(WebSocketConnector connector, Token token) {
    actionsDispatcher.performAction(connector, token);
  }

  /**
   * Gets action dispatcher with actions and handlers inside.
   *
   * @return actions dispatcher.
   */
  public ActionsDispatcher getActionsDispatcher() {
    return actionsDispatcher;
  }
}
